package com.yesep.learn.design.behavioral.visitor;

/**
 * 访问者模式（Visitor Pattern）是一种行为型设计模式，它允许一个对象在不修改其结构的前提下增加新的操作。
 * 就增加功能来说，访问者模式相比生成子类更为灵活。
 * 访问者模式适用于数据结构相对稳定的系统，但作用于这些数据结构的操作经常改变的场景。
 * <p>
 * 一、定义与结构
 * 定义：访问者模式表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
 * <p>
 * 结构：访问者模式主要包含以下几个角色：
 * <p>
 * 访问者（Visitor）角色：为该对象结构中具体元素角色声明一个访问操作接口，该操作接口的名字和参数标识了将要访问的具体元素角色。这样访问者就可以通过该元素的特定接口直接访问它。
 * 具体访问者（Concrete Visitor）角色：实现每个由访问者（Visitor）声明的操作。
 * 元素（Element）角色：声明一个接受操作（Accept），这个操作为一个访问者（Visitor）对象提供了一个访问其元素的接口。
 * 具体元素（Concrete Element）角色：实现接受（Accept）操作，这个操作就是调用访问者的访问该元素类的方法。
 * 对象结构（Object Structure）角色：这是能枚举它的元素的一个类（如 List 或 Set）。
 * 能枚举每个元素；可以提供一个高层的接口以允许访问者访问它的元素；可以是一个复合（Composite）模式或装饰（Decorator）模式的结构，但不需要是。
 * 二、工作原理
 * 访问者模式的工作原理是通过将操作封装在访问者对象中，然后将访问者对象传递给被访问的对象结构中的每一个元素。
 * 每个元素通过接受方法接收访问者对象，并调用访问者对象中的方法来执行具体的操作。这样，就可以在不修改元素类代码的前提下，通过增加新的访问者类来增加新的操作。
 * <p>
 * 三、优点
 * 增加新的操作很容易：就增加新的操作而言，访问者模式使得修改元素类变得非常容易。如果增加新的操作，只需要增加一个新的访问者类即可，现有元素类的代码无须修改。
 * 将有关的行为集中到一个访问者对象中：如果一个对象结构包含很多类的对象，有太多的客户端代码访问这些对象的类，那么可以考虑使用访问者模式。
 * 访问者模式可以把这些类相关的操作集中在一个访问者类中。
 * 可以访问到复合对象的内部状态：如果需要将一个复合对象（如组合模式）的组成部分对象作为不同的类型处理，可以使用访问者模式。
 * 四、缺点
 * 增加新的元素类很困难：在访问者模式中，每增加一个新的元素类，都需要在所有的具体访问者类中增加相应的具体操作，这违反了“开闭原则”。
 * 破坏封装：访问者模式要求访问者对象访问并调用每一个元素对象的操作，这可能会暴露元素对象的内部实现细节和状态。
 * 五、应用场景
 * 访问者模式适用于以下场景：
 * <p>
 * 对象结构中对象对应的类很少改变，但经常需要在此对象结构上定义新的操作。
 * 需要对一个对象结构中的对象进行很多不同的并且不相关的操作，而需要避免让这些操作“污染”这些对象的类。
 * 将这些操作封装在一个访问者对象中，可以使它们独立于对象的类来变化。
 * 六、实例
 * 以公司结构为例，可以定义一个员工类（Employee）作为元素角色，包括多种类型的员工（如普通员工、经理等）。
 * 然后定义一个访问者接口（Visitor），并实现具体的访问者类，如绩效评估者（PerformanceEvaluator）和工资计算者（SalaryCalculator）。
 * 这样，就可以在不修改员工类代码的前提下，通过增加新的访问者类来增加新的操作，如绩效评估或工资计算。
 */
public class Main {
    public static void main(String[] args) {
        Zoo zoo = new Zoo();
        Dog dog = new Dog();
        dog.setName("汪汪");
        zoo.add(dog);
        Cat cat = new Cat();
        cat.setName("喵喵");
        zoo.add(cat);

        OwnerVisitor ownerVisitor = new OwnerVisitor();
        zoo.action(ownerVisitor);

        GuestVisitor guestVisitor = new GuestVisitor();
        zoo.action(guestVisitor);
    }
}
